// Copyright (c) 2010, Keith Nixon, Sales Engineering, Salesforce.com Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// Neither the name of the salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 
// Assumptions are as follows:
//    o   Build to allow this method of allocation to be altered at a later date. eg prioritise some zones over others.
//    o   Assumes approx. same number of accounts in each zone for demo
//    o   Assume working days M-F for demo.  Will need full calendar awareness at a later date.


public class RoutePlanner
{
    //The routes that will be inserted
    private List<Daily_Route__c> routes;
    
    //The sales person id
    private Id salesPersonId;
    
    //The sales person id
    private Sales_Person__c contextSalesPerson;
    
    //The context Daily_Cycle__c
    private Cycle__c contextCycle;
    
    //This is the zone map
    private Map<Id,Zone__c> zoneMap;
    
    public RoutePlanner(ApexPages.StandardController stdController)
    {
        this.contextCycle = (Cycle__c) stdController.getRecord();
        this.salesPersonId = ApexPages.currentPage().getParameters().get('salesPersonId');
        this.contextSalesPerson = [
            Select Visits_Per_Day__c, Day_Start__c, Time_Per_Visit__c from Sales_Person__c Where Id =: salesPersonId LIMIT 1
        ];
    }
    
    //This will generate the daily routes for a sales person
    //based on the sales person's number of visits per day
    public PageReference generateDailyRoutes()
    {
        //The page reference to the cycle record
        PageReference pr = new PageReference('/' + contextCycle.Id);
    
        //query for all the zones and accounts for this sales person
        List<Zone__c> salesPersonsZones = [
            Select z.Id, z.Name, z.Priority__c, z.Territory__c, z.Territory__r.Sales_Person__r.Visits_Per_Day__c, 
            (Select Id, Name From Accounts__r Order By Annual_Sales__c Desc)
            From Zone__c z
            Where z.Territory__r.Sales_Person__c =: salesPersonId
            ORDER BY z.Priority__c ASC
        ];
        
        if(!salesPersonsZones.isEmpty())
        {
            //populate our zone map
            zoneMap = new Map<Id,Zone__c>();
            
            for(Zone__c z : salesPersonsZones)
            {
                zoneMap.put(z.Id,z);
            }
        
            List<Daily_Route__c> routes = new List<Daily_Route__c>();
            Integer zoneIndex = 0;
        
            //create a route for each day in the period__c
            for(Integer i=0; i<contextCycle.Period__c; i++)
            {
                Zone__c currentZone = salesPersonsZones.get(zoneIndex);

                Daily_Route__c route = new Daily_Route__c();
                
                route.Name = contextCycle.Name + ' - ' + currentZone.Name + ' - ' + contextCycle.Start_Date__c.addDays(i).format();
                route.Cycle__c = contextCycle.Id;
                route.Date__c = contextCycle.Start_Date__c.addDays(i);
                route.Visits_Planned__c = currentZone.Territory__r.Sales_Person__r.Visits_Per_Day__c;
                route.Zone__c = currentZone.Id;
                
                routes.add(route);
                
                //if we are at the last index of the zones list, start from 
                //the beginning of the zones list again
                if(i == (salesPersonsZones.size() - 1))
                {
                    zoneIndex = 0;
                }
                else zoneIndex++;
            }
            
            if(!routes.isEmpty())
            {
                insert routes;
            }
            
            List<Outlet_Visit__c> outletVisits = new List<Outlet_Visit__c>();
            
            //now we create our outlet visit records for the number of
            //days in the period based on the number of visits per day 
            //that the sales person can carry out.
            for(Daily_Route__c route : routes)
            {
                Integer numVisitsPerDayCounter = 0;
                DateTime startDateTime = contextSalesPerson.Day_Start__c;
            
                //we get the accounts for the associated zone
                for(Account acc : zoneMap.get(route.Zone__c).accounts__r)
                {
                    //The accounts are ordered by priority. We will get the top
                    //X accounts where X = the number of visits a rep can carry
                    //out in a single day.
                    if(numVisitsPerDayCounter < contextSalesPerson.Visits_Per_Day__c)
                    {   
                        //create our outlet visit record and add to the list
                        Outlet_Visit__c ov = new Outlet_Visit__c();
                        
                        ov.Account__c = acc.Id;
                        ov.Route__c = route.Id;
                        ov.Planned_Duration__c = contextSalesPerson.Time_Per_Visit__c;
                        
                        //calculate the start time
                        Integer startYear = route.Date__c.year();
                        Integer startMonth = route.Date__c.month();
                        Integer startDay = route.Date__c.day();
                        
                        //add the time per vist for the sales person on to 
                        //their day start time
                        startDateTime = startDateTime.addMinutes(contextSalesPerson.Time_Per_Visit__c.intValue());
                        
                        Integer startHour = startDateTime.hour();
                        Integer startMinute = startDateTime.minute();
                        
                        ov.Planned_time__c = DateTime.newInstance(startYear,startMonth,startDay,startHour,startMinute,0);
                        
                        outletVisits.add(ov);
                        
                        numVisitsPerDayCounter++;
                    }
                    else break;
                }//close account for loop
            }//close route loop
            
            if(!outletVisits.isEmpty())
            {
                insert outletVisits;
                pr.setRedirect(true);
            }
        }
        
        return pr;
    }
}